/*
 * dbDir Server
 */
package org.dbdir;

import java.io.Console;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.sql.Connection;
import org.dbdir.connections.TCPSession;
import org.dbdir.core.AccessData;
import org.dbdir.core.AutoSetup;
import org.dbdir.core.Functions;
import org.dbdir.core.ServerAuth;
import org.dbdir.core.datastore.db.SQLManager;

/**
 *
 * @author thotheolh
 */
public class Server {

    private String username = null;
    private String connPath = "jdbc:h2:file:~/DBStore/dbDir;CIPHER=AES";
    private Connection dbConn = null;
    private String mode = "CLI"; //CLI is native console, GUI is GUI
    private Console input = System.console();
    private boolean isAuthFlag = false;
    private AccessData access = null;
    private SQLManager sql = null;
    private Server instance = null;
    private boolean isListening = true;
    ServerSocket serverSocket = null;
    private Functions funct = new Functions();
    private org.dbdir.core.Logger log = new org.dbdir.core.Logger();

    public Server() {
        instance = this;
    }

    public void initServer() {
        output("Welcome to dbDir Server\n");
        output("~~~~~~~~~~~~~~~~~~~~~~~\n\r");
        doAuth();
        if (isAuthFlag == true) {
            sql = new SQLManager(getDbConn());
            checkSetup();
            access = new AccessData(sql);
            instance = this;
            output("Type 'help'or '?' for help.");
            output("Type 'quit'or 'exit' or 'bye' to exit.\n");
            new ServerConsole(sql, instance).start();
            log.log(0, "Server started OK.");
            createTCPSession();
        }
    }

    public void output(String message) {
        if (mode.equals("CLI")) {
            System.out.print(message);
        }
        if (mode.equals("GUI")) {
            //relay data to GUI
            throw new UnsupportedOperationException("GUI interface is not yet implemented");
        }
    }

    public void doAuth() {
        ServerAuth auth = new ServerAuth();
        do {
            this.setUsername(input.readLine("\n\rUsername: "));
            auth.setConnPath(connPath);
            auth.setUsername(username);
            char[] passA = input.readPassword("\n\rPassword: ");
            String pass = new String(passA);
            // output("password you typed: "+pass); diagnostic
            auth.setPassword(pass + " " + pass);
            // output("auth pass: "+auth.getPassword()); diagnostic
            if (auth.doAuth() == true) {
                isAuthFlag = true;
                setDbConn(auth.getDbConn());
            } else {
                output("\n\r(!) Authentication failed ! Please try again...\n\r");
            }
        } while (isAuthFlag == false);
    }

    /**
     * @return the username
     */
    public String getUsername() {
        return username;
    }

    /**
     * @param username the username to set
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * @return the connPath
     */
    public String getConnPath() {
        return connPath;
    }

    /**
     * @param connPath the connPath to set
     */
    public void setConnPath(String connPath) {
        this.connPath = connPath;
    }

    /**
     * @return the dbConn
     */
    public Connection getDbConn() {
        return dbConn;
    }

    /**
     * @param dbConn the dbConn to set
     */
    public void setDbConn(Connection dbConn) {
        this.dbConn = dbConn;
    }

    public void doExitServer() {
        if (sql == null) {
            output("(!) WARNING: SQL: NULL\n");
        }
        sql.close();
        if (serverSocket != null || (!serverSocket.isClosed())) {
            try {
                serverSocket.close();
            } catch (IOException ex) {
                log.log(1, ex.getMessage());
            }
        }
        log.log(0, "Server exit.");
        System.exit(0);
    }

    public Server getServerInstance() {
        return this.instance;
    }

    public Console getInput() {
        return input;
    }

    public SQLManager getSQL() {
        return sql;
    }

    public AccessData getAccessData() {
        return access;
    }

    /**
     * Create new TCP Session. Only listens to port 10999
     */
    public void createTCPSession() {
        try {
            try {
                serverSocket = new ServerSocket(10999);
            } catch (IOException e) {
                System.err.println("Could not listen on port: 10999.\n");
                log.log(1, e.getMessage());
                System.exit(-1);
            }
            while (isListening) {
                new TCPSession(instance, serverSocket.accept(), access).start();
            }
            serverSocket.close();
        } catch (IOException ex) {
            log.log(1, ex.getMessage());
        }
    }

    /**
     * @return the funct
     */
    public Functions getFunct() {
        return funct;
    }

    /**
     * @param funct the funct to set
     */
    public void setFunct(Functions funct) {
        this.funct = funct;
    }

    /**
     * For dbDir 0.2 and onwards
     */
    private void checkDir() {
        File rootDir = new File(".dbDir");
        if (!rootDir.exists()) {
            rootDir.mkdir();
        }
    }

    private void checkSetup() {
        AutoSetup setup = new AutoSetup(this.getLog(), this.getDbConn());
        setup.init("script/setup/dbdir-setup.sql");
    }

    /**
     * @return the log
     */
    public org.dbdir.core.Logger getLog() {
        return log;
    }

    /**
     * @param log the log to set
     */
    public void setLog(org.dbdir.core.Logger log) {
        this.log = log;
    }
}
